<?php

namespace common;


use Carbon\Carbon;
use yii\log\FileTarget;
use yii\log\Logger;

class Tools
{
    /**
     * 打印任意多参数并停止
     */
    public static function stop()
    {
        $arr = func_get_args();
        foreach ($arr as $k => $v) {
            var_dump($v);
        }
        exit;
    }

    /**
     * @param $string
     * @param null $name
     * @param int $rewrite
     * @throws \Exception
     */
    public static function wolog($string, $name = null, $rewrite = 0)
    {
        $dir = YII_PROJECT_ROOT."/common/runtime/wolog";
        $dir = str_replace("//wolog", "/wolog", $dir);
        if (!is_dir($dir)) {
            static::createDirectory($dir);
        }
        if ($name == null) {
            $name = 'wolog.log';
            $rewrite = 1;
        } else {
            $name .= '.log';
        }
        $path = $dir.DIRECTORY_SEPARATOR.$name;
        $string .= "\r\n";
        if ($rewrite) {
            file_put_contents($path, $string);
        }else{
            file_put_contents($path, $string, FILE_APPEND);
        }
    }

    /**
     * @param $msg
     * @param null $log_name
     * @throws \yii\base\Exception
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\log\LogRuntimeException
     */
    public static function yiiLog($msg, $log_name = null)
    {
        if (!$log_name){
            $log_name = \Yii::$app->controller->route;
        }else{
            $log_name = \Yii::$app->controller->route.DIRECTORY_SEPARATOR.$log_name;
        }
        $log = New FileTarget();
        $log->logFile = \Yii::getAlias('@common/runtime/logs/') . \Yii::$app->id . DIRECTORY_SEPARATOR . "{$log_name}.log";
        $dir = dirname($log->logFile);
        if (!is_dir($dir))static::createDirectory($dir);
        $log->messages[] = [$msg, Logger::LEVEL_INFO, 'tool-log', time()];
        $log->export();
    }

    /**
     * 获取文件列表
     * @param string $dir
     * @param bool $recursive
     * @return array
     */
    public static function listDir($dir, $recursive = true)
    {
        $result = array();
        if (is_dir($dir)) {
            $file_dir = scandir($dir);
            foreach ($file_dir as $file) {
                if ($file == '.' || $file == '..') {
                    continue;
                } else {
                    $filePath = $dir . DIRECTORY_SEPARATOR . $file;
                    array_push($result, $filePath);
                    if (is_dir($filePath) && $recursive){
                        $result = array_merge($result, self::listDir($filePath, $recursive));
                    }
                }
            }
        }
        return $result;
    }

    /**
     * 判断是否为时间戳
     * @param int $timestamp
     * @return bool
     */
    public static function isTimestamp($timestamp)
    {
        if (!is_numeric($timestamp)){
            return false;
        }else{
            $timestamp = intval($timestamp);
        }
        if (strtotime(date('Y-m-d H:i:s', $timestamp)) === $timestamp) {
            return true;
        } else return false;
    }

    /**
     * 删除目录下的所有文件和文件夹
     * @param string $path
     */
    public static function deldir($path){
        //如果是目录则继续
        if(is_dir($path)){
            //扫描一个文件夹内的所有文件夹和文件并返回数组
            $p = scandir($path);
            foreach($p as $val){
                //排除目录中的.和..
                if($val !="." && $val !=".."){
                    $pv = $path . DIRECTORY_SEPARATOR . $val;
                    //如果是目录则递归子目录，继续操作
                    if(is_dir($pv)){
                        //子目录中操作删除文件夹和文件
                        self::deldir($pv);
                        //目录清空后删除空文件夹
                        rmdir($pv);
                    }else{
                        //如果是文件直接删除
                        unlink($pv);
                    }
                }
            }
        }
    }

    /**
     * 删除目录
     * @param string $path
     */
    public static function removeDir($path)
    {
        self::deldir($path);
        rmdir($path);
    }

    /**
     * 生成不带横杠的UUID
     * @return string
     */
    public static function genuuid(){
        return sprintf('%04x%04x%04x%04x%04x%04x%04x%04x',
            // 32 bits for "time_low"
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),

            // 16 bits for "time_mid"
            mt_rand(0, 0xffff),

            // 16 bits for "time_hi_and_version",
            // four most significant bits holds version number 4
            mt_rand(0, 0x0fff) | 0x4000,

            // 16 bits, 8 bits for "clk_seq_hi_res",
            // 8 bits for "clk_seq_low",
            // two most significant bits holds zero and one for variant DCE1.1
            mt_rand(0, 0x3fff) | 0x8000,

            // 48 bits for "node"
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
        );
    }

    /**
     * @param $str
     * @return bool
     */
    public static function checkMobile($str)
    {
//        $pattern = "/^(13|15)d{9}$/";
        $pattern = "/^1[345678]{1}\d{9}$/";
        if (preg_match($pattern, $str))
        {
            Return true;
        }
        else
        {
            Return false;
        }
    }

    /**
     * 获取文件夹大小
     *
     * @param string $dir 根文件夹路径
     * @return int
     */
    public static function getDirSize($dir)
    {
        $handle = opendir($dir);
        $sizeResult = 0;
        while (false !== ($folderOrFile = readdir($handle))) {
            if ($folderOrFile != "." && $folderOrFile != "..") {
                if (is_dir("$dir/$folderOrFile")) {
                    $sizeResult += self::getDirSize("$dir/$folderOrFile");
                } else {
                    $sizeResult += filesize("$dir/$folderOrFile");
                }
            }
        }
        closedir($handle);
        return $sizeResult;
    }

    function isEmail($email){
        if (preg_match("/^[0-9a-zA-Z]+@(([0-9a-zA-Z]+)[.])+[a-z]{2,4}$/i", $email)) {
            return true;
        } else{
            return false;
        }
    }

    /**
     * 判断字符串是否为json，若是输出json
     * @param string $str Json字符串
     * @param bool $assoc 是否返回关联数组。默认返回对象
     * @return array|bool|object 成功返回转换后的对象或数组，失败返回 false
     */
    public static function isJson($str, $assoc = true)
    {
        $data = json_decode($str, $assoc);
        if (($data && is_object($data)) || (is_array($data) && !empty($data))) {
            return $data;
        }
        return false;
    }

    /**
     * 转json
     * @param $data
     * @return string Json字符串
     */
    public static function toJson($data)
    {
        return json_encode($data, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 判断字符串是否为query，若是输出query数组
     * @param string $str Json字符串
     * @param bool $assoc 是否返回关联数组。默认返回对象
     * @return array|bool|object 成功返回转换后的对象或数组，失败返回 false
     */
    public static function isQuery($str)
    {
        parse_str($str, $data);
        if (is_array($data) && !empty($data)) {
            return $data;
        }
        return false;
    }

    /**
     * 转query字符串
     * @param $data
     * @return string query字符串
     */
    public static function toQuery($data)
    {
        return urldecode(http_build_query($data));
    }

    /**
     * 处理数组值的外层
     * @param $arr
     * @param string $type
     * @param string $outStr
     * @return mixed
     */
    public static function outerStr($arr, $type = "+", $outStr = "()")
    {
        foreach ($arr as $k => &$v) {
            $v = "({$v})";
        }
        return $arr;
    }

    /**
     * @return string
     */
    public static function getDeviceType()
    {
        //全部变成小写字母
        $agent = strtolower($_SERVER['HTTP_USER_AGENT']);
        $type ='other';
        //分别进行判断
        if(strpos($agent,'iphone') || strpos($agent,'ipad'))
        {
            $type ='ios';
        }
        if(strpos($agent,'android'))
        {
            $type ='android';
        }
        return$type;
    }

    /**
     * 判断是否命令行
     * @return bool
     */
    public static function isCli()
    {
        return preg_match("/cli/i", php_sapi_name()) ? true : false;
    }

    /**
     * unicode编码转汉字
     * @param $str
     * @return null|string|string[]
     */
    public static function decodeUnicode($str)
    {
        $reg = <<<REGEXP
/\\\\u([0-9a-f]{4})/i
REGEXP;
        $r = preg_replace_callback($reg, function ($matches){
            $x = mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
            return $x;
        }, $str);
        return $r;
    }

    /**
     * 生成随机字符串
     * @param $length
     * @return string
     */
    public static function generateRandomStr($length = 32)
    {
        // 定义所有可能的字符集
        $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';
        // 将字符集打乱并取前指定长度的子串作为随机数
        return substr(str_shuffle($characters), 0, $length);
    }

    /**
     * @param int $len 必须大于等于27
     * @return string
     * @throws \yii\base\Exception
     */
    public static function generateHasDateUniqueString($len = 32)
    {
        $_len = $len - 27;
        if ($_len < 0){
            throw new \Exception("长度异常");
        }
        $str = uniqid().date("YmdHis").self::generateRandomStr($_len);
        return $str;
    }

    /**
　　  * 下划线转驼峰
　　  * 思路:
　　  * step1.原字符串转小写,原字符串中的分隔符用空格替换,在字符串开头加上分隔符
　　  * step2.将字符串中每个单词的首字母转换为大写,再去空格,去字符串首部附加的分隔符.
　　  */
    public static function camelize($uncamelized_words, $separator='_')
    {
        $uncamelized_words = $separator. str_replace($separator, " ", strtolower($uncamelized_words));
        return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator );
    }

    /**
     * 驼峰命名转下划线命名
     * 思路:
     * 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
     * @param $camelCaps
     * @param string $separator
     * @return string
     */
    public static function uncamelize($camelCaps, $separator='_')
    {
        return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
    }

    /**
     * 获取微妙时间戳
     * @return string
     */
    public static function getMicrosecondTimestamp()
    {
        $x = microtime();
        list($m, $s) = explode(' ', $x);
        $y = $m * 1000000;
        $y = (int)$y;
        return $s . $y;
    }

    /**
     * 获取当前请求协议
     * @return string
     */
    public static function getCurrentScheme()
    {
        if(isset($_SERVER['HTTP_X_CLIENT_SCHEME'])){
            $scheme = $_SERVER['HTTP_X_CLIENT_SCHEME'] . '://';
        }elseif(isset($_SERVER['REQUEST_SCHEME'])){
            $scheme = $_SERVER['REQUEST_SCHEME'] . '://';
        }else{
            $scheme = 'http://';
        }
        return $scheme;
    }

    /**
     * @param $path
     * @param int $mode
     * @param bool $recursive
     * @return bool
     * @throws \Exception
     */
    public static function createDirectory($path, $mode = 0775, $recursive = true)
    {
        if (is_dir($path)) {
            return true;
        }
        $parentDir = dirname($path);
        // recurse if parent dir does not exist and we are not at the root of the file system.
        if ($recursive && !is_dir($parentDir) && $parentDir !== $path) {
            static::createDirectory($parentDir, $mode, true);
        }
        try {
            if (!mkdir($path, $mode)) {
                return false;
            }
        } catch (\Exception $e) {
            if (!is_dir($path)) {
                throw new \Exception("Failed to create directory \"$path\": " . $e->getMessage(), $e->getCode(), $e);
            }
        }
        try {
            return chmod($path, $mode);
        } catch (\Exception $e) {
            throw new \Exception("Failed to change permissions for directory \"$path\": " . $e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * 获取base64图片信息
     * @param $base64img
     * @param $type
     * @return int|mixed|void
     */
    public static function getBase64ImgInfo($base64img,$type)
    {
        if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64img, $result)){
            if ($type=='type'){
                $img_result = $result[2];
            }elseif ($type=='size'){
                $base_img = str_replace($result[1], '', $base64img);
                $base_img = str_replace('=','',$base_img);
                $img_len = strlen($base_img);
                $file_size = intval($img_len - ($img_len/8)*2); // B
//                $img_result = number_format(($file_size/1024),2);//KB
                $img_result = $file_size;//KB
            }
            return $img_result;
        }
    }

    /**
     * 时间戳转化为日期格式
     * @param $timestamp
     * @return false|string
     */
    public static function toDate($timestamp = null)
    {
        if (!$timestamp) $timestamp = Carbon::now()->timestamp;
        $t10 = substr($timestamp, 0, 10);
        $mt = str_replace($t10, "", $timestamp);
        $mt = str_replace(".", "", $mt);
        $d10 = date("Y-m-d H:i:s", $t10);
        if ($mt){
            $mt = substr($mt, 0, 3);
            $d = $d10.".{$mt}";
        }else{
            $d = $d10;
        }
        return $d;
    }

    /**
     * 获取文件扩展名
     * @param string $file
     * @return string
     */
    public static function getFileExt($file)
    {
        $ext = pathinfo($file)['extension'];
        $ext = strtolower($ext);
        return $ext;
    }

    /**
     * 判断是否为Linux操作系统
     * @return bool
     */
    public static function isLinux() {
        return (strtoupper(substr(PHP_OS, 0, 5)) === 'LINUX');
    }

    /**
     * 判断是否为Windows操作系统
     * @return bool
     */
    public static function isWindows() {
        return (strtoupper(substr(PHP_OS, 0, 7)) === 'WINNT' || strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
    }

    /**
     * 判断是否为Mac操作系统
     * @return bool
     */
    public static function isMac() {
        return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
    }

    public static function deleteEmptyFolders($dir) {
        // 打开指定目录
        if ($handle = opendir($dir)) {
            // 遍历目录中的所有文件和子文件夹
            while (false!== ($entry = readdir($handle))) {
                // 跳过当前目录和上级目录
                if ($entry != "." && $entry != "..") {
                    $path = $dir . '/' . $entry;
                    // 如果是文件夹
                    if (is_dir($path)) {
                        // 递归调用函数删除子文件夹
                        self::deleteEmptyFolders($path);
                    }
                }
            }
            // 关闭目录句柄
            closedir($handle);

            // 再次检查文件夹是否为空
            if (count(scandir($dir)) == 2) {
                // 删除空文件夹
                rmdir($dir);
            }
        }
    }

    /**
     * 递归获取文件夹下的所有文件夹
     * @param $dir
     * @param $folders
     * @param array $leafFolders
     * @return void
     */
    public static function getFolderNames($dir, &$folders, &$leafFolders) {
        // 获取当前目录下的所有文件和文件夹
        $items = scandir($dir);
        // 过滤掉 "." 和 ".."
        $items = array_diff($items, array('.', '..'));
        $hasSubFolders = false; // 用于标记当前文件夹是否有子文件夹
        foreach ($items as $item) {
            // 拼接完整的文件夹路径
            $itemPath = $dir . '/' . $item;
            // 检查是否是文件夹
            if (is_dir($itemPath)) {
                $hasSubFolders = true; // 标记当前文件夹有子文件夹
                // 将文件夹名添加到数组中
                $folders[] = $itemPath;
                // 递归调用，继续遍历子文件夹
                Tools::getFolderNames($itemPath, $folders, $leafFolders);
            }
        }
        // 如果当前文件夹没有子文件夹，则将其路径添加到结果数组中
        if (!$hasSubFolders) {
            $leafFolders[] = $dir;
        }
    }

    /**
     * 判断目录是否为空
     * @param string $dir 目录路径
     * @return bool 如果目录为空返回 true，否则返回 false
     */
    public static function isDirectoryEmpty($dir) {
        // 检查是否是有效的目录
        if (!is_dir($dir)) {
            throw new \InvalidArgumentException("指定路径不是一个有效的目录：$dir");
        }
        // 打开目录句柄
        $handle = opendir($dir);
        // 遍历目录
        while (($item = readdir($handle)) !== false) {
            // 跳过 "." 和 ".."（当前目录和上一级目录）
            if ($item !== "." && $item !== "..") {
                // 如果目录中有其他内容，则返回 false
                closedir($handle);
                return false;
            }
        }
        // 关闭目录句柄
        closedir($handle);
        // 如果目录为空，则返回 true
        return true;
    }

    /**
     * 判断目录是否有子目录
     * @param string $directory 目录路径
     * @return bool 如果目录中有子目录返回 true，否则返回 false
     */
    public static function hasSubdirectories($directory) {
        // 获取目录中的所有条目
        $entries = scandir($directory);
        // 遍历条目，检查是否存在子目录
        foreach ($entries as $entry) {
            if ($entry !== '.' && $entry !== '..') {
                $path = $directory . '/' . $entry;
                if (is_dir($path)) {
                    return true; // 找到子目录
                }
            }
        }
        return false; // 没有找到子目录
    }
}
